#include <sstream>
#include <spdlog/spdlog.h>
#include "STL_thread.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
namespace simpleThread {
    STLThread::STLThread(simpleThread::TaskQueue *queue) {
        this->queue = queue;
    }

    STLThread::~STLThread() {
        if (this->workThread != nullptr) {
            this->shutdown();
            delete this->workThread;
        }
        spdlog::debug("Destroy thread {}", this->workThreadName);
    }

    void STLThread::start() {
        this->workThread = new std::thread(&STLThread::execute, std::ref(*this));
        std::ostringstream oss;
        oss << this->workThread->get_id();
        this->workThreadName = oss.str();

        spdlog::debug("create thread {}", this->workThreadName);
        this->execInitCall();
    }

    void STLThread::execute() {
        while (this->state != close) {
            auto task = this->queue->pull();
            if (task != nullptr) {
                STLThread::work(task);
                this->executeTime = std::time(nullptr);
                continue;
            }
            if (this->isBreak()) {
                break;
            }
        }
        this->execExitCall();
        spdlog::debug("exit thread {}", this->workThreadName);
    }

    void STLThread::execInitCall() {
        if (this->initCall != nullptr) {
            this->initCall(*this);
        }
    }

    void STLThread::execExitCall() {
        this->state = close;
        if (this->exitCall != nullptr) {
            this->exitCall(*this);
        }
    }

    void STLThread::work(const std::function<void()> &task) noexcept {
        try {
            task();
        } catch (std::exception &e) {
            spdlog::warn("execute task error: ", e.what());
        }
    }

    std::thread::id STLThread::getId() {
        if (this->workThread != nullptr) {
            return this->workThread->get_id();
        }
        return {};
    }

    void STLThread::join() {
        this->joinState = true;
        if (this->workThread != nullptr && this->workThread->joinable()) {
            this->workThread->join();
        }
    }

    void STLThread::shutdown() {
        this->state = close;
        this->join();
    }

    void STLThread::timeout() {
        this->state = ThreadState::timeout;
    }

    bool STLThread::isBreak() {
        if (this->checker != nullptr && this->checker(*this)) {
            return true;
        }
        if (this->joinState && this->queue->size() <= 0) {
            return true;
        }
        if (this->state == ThreadState::close || this->state == ThreadState::timeout) {
            return true;
        }
        return false;
    }

    time_t STLThread::getExecuteTime() const {
        return executeTime;
    }

    volatile ThreadState STLThread::getState() const {
        return state;
    }

    void STLThread::setChecker(const std::function<bool(STLThread &)> &function) {
        this->checker = function;
    }

    void STLThread::setInitCall(const std::function<bool(STLThread &)> &function) {
        this->initCall = function;
    }

    void STLThread::setExitCall(const std::function<bool(STLThread &)> &function) {
        this->exitCall = function;
    }
}
#pragma clang diagnostic pop